# Create your views here.

from crmv2.survey.models import Survey, SurveyNotifySchedule, SurveyInvitation, \
    SurveyAnswer, SurveyTemplate, SurveyChoice, SurveyQuestion
from django.db import models
import operator
from django.core.paginator import Paginator
from django.shortcuts import render_to_response
from django.utils.http import urlencode
from crmv2.survey.forms import CreateFormForSurveyTemplate, SurveyForm
from django.http import HttpResponseRedirect, HttpResponse
from crmv2.survey.admin import SurveyAdmin, SurveyInvitationAdmin

from django.contrib.admin.filterspecs import FilterSpec
from pprint import pprint
from crmv2.models import Student
from crmv2.admin import StudentAdmin

PAGE = 'page'
SEARCH = 'q'
ALL = 'all'
ORDER = 'o'
ORDER_TYPE = 'ot'

class IncorrectLookupParameters(Exception):
    pass

def extract_ordering(params):
    order_type = 'asc'
    field_name = None
    if ORDER in params:
        field_name = params[ORDER]
    if ORDER_TYPE in params and params[ORDER_TYPE] in ('asc', 'desc'):
        order_type = params[ORDER_TYPE]

    return field_name, order_type

def extract_filter(params):
    filters = {}
    for k, v in params.items():
        filters[str(k)] = str(v)

    for i in (ALL, PAGE, SEARCH, ORDER, ORDER_TYPE):
        if i in filters:
            del filters[i]
    for key, value in filters.items():
        if key.endswith('__in'):
            filters[str(key)] = value.split(',')
        if key.endswith('__isnull'):
            if value.lower() in ('', 'false'):
                filters[str(key)] = False
            else:
                filters[str(key)] = True

    return filters

def apply_search(qs, query, fields):
    def construct_search(field_name):
        if field_name.startswith('^'):
            return "%s__istartswith" % field_name[1:]
        elif field_name.startswith('='):
            return "%s__iexact" % field_name[1:]
        elif field_name.startswith('@'):
            return "%s__search" % field_name[1:]
        else:
            return "%s__icontains" % field_name

    for bit in query.split():
        or_queries = [models.Q(**{construct_search(field_name) : bit}) for field_name in fields]
        qs = qs.filter(reduce(operator.or_, or_queries))
    return qs.distinct()

class header:
    pass

def construct_headers(query, fields, order_field, order_type):
    headers = []
    for f in fields:
        h = header()
        params = dict(query)
        params[ORDER] = f.name
        params[ORDER_TYPE] = 'asc'
        if f.name == order_field and order_type == 'asc':
            params[ORDER_TYPE] = 'desc'
        h.name = f.verbose_name
        h.url = '?%s' % urlencode(params)
        if f.name == order_field:
            h.class_attrib = 'sorted %s' % (order_type == 'asc' and 'ascending' or 'descending')
        else:
            h.class_attrib = ''

        headers.append(h)

    return headers

class cl_for_filter_spec:
    def __init__(self, params):
        self.params = params

    def get_query_string(self, new_params = None, remove = None):
        if new_params is None: new_params = {}
        if remove is None: remove = []
        p = self.params.copy()
        for r in remove:
            for k in p.keys():
                if k.startswith(r):
                    del p[k]
        for k, v in new_params.items():
            if v is None:
                if k in p:
                    del p[k]
            else:
                p[k] = v
        return '?%s' % urlencode(p)

def page_links(page, params):
    if page.paginator.num_pages <= 1:
        return []

    q = cl_for_filter_spec(params)
    result = []
    if page.number == 1:
        result.append({'text' : 'First', 'url' : ''})
    else:
        result.append({'text' : 'First', 'url' : q.get_query_string({'page' : 1}, ['page'])})

    if page.has_previous():
        result.append({'text': 'Prev', 'url' : q.get_query_string({'page' : page.previous_page_number()}, ['page'])})

    result.append({'text' : page.number, 'url' : ''})

    if page.has_next():
        result.append({'text' : 'Next', 'url' : q.get_query_string({'page' : page.next_page_number()}, ['page'])})

    if page.number != page.paginator.num_pages:
        result.append({'text' : 'Last', 'url' : q.get_query_string({'page' : page.paginator.num_pages}, ['page'])})

    return result

def pages(page, params):
    if page.paginator.num_pages <= 1:
        return []
    q = cl_for_filter_spec(params)
    return [{'text' : i, 'url' : q.get_query_string({'page' : i}, ['page'])} for i in page.paginator.page_range]

def paginator_result(page, params):
    result = {}
    result['links'] = page_links(page, params)
    result['record_number'] = page.paginator.count
    result['pages'] = pages(page, params)
    result['current'] = page.number
    result['record_in_page'] = page.object_list.count()

    return result

def survey_list(request):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    try:
        page_number = int(request.GET.get(PAGE, '1'))
    except ValueError:
        page_number = 1

    show_all = ALL in request.GET
    params = dict(request.GET.items())
    order_field, order_type = extract_ordering(params)
    query = request.GET.get(SEARCH, '')

    filter_words = extract_filter(params)
    surveys = Survey.objects.all().filter(**filter_words)

    if query:
        surveys = apply_search(surveys, query, ('title', 'owner__name'))

    if order_field:
        orderby = '%s%s' % (order_type == 'desc' and '-' or '', order_field)
        surveys = surveys.order_by(orderby)

    p_result = None
    paginator = Paginator(surveys, 30)
    if not show_all:
        page = paginator.page(page_number)
        surveys = page.object_list
        p_result = paginator_result(page, params)
    else:
        p_result = {'record_in_page' : surveys.count()}

    if PAGE in params:
        del params[PAGE]

    filter_text = ''
    for f in [FilterSpec.create(Survey._meta.get_field(f), request, params, Survey, SurveyAdmin) for f in SurveyAdmin.list_filter]:
        filter_text += f.output(cl_for_filter_spec(params))

    headers = construct_headers(params, [Survey._meta.get_field(f) for f in SurveyAdmin.list_display],
                                order_field, order_type)

    return render_to_response('survey_list.template', {
                'survey_list' : surveys,
                'paginator_result' : p_result,
                'show_all' : show_all,
                'headers' : headers,
                'filters' : filter_words,
                'filter_text' : filter_text,
                })

def survey_add(request):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    if request.method == 'POST':
        form = SurveyForm(request.POST)
        if form.is_valid():
            survey = form.save()
            for f in ['notify_1', 'notify_2', 'notify_3']:
                if form.cleaned_data[f]:
                    notify = SurveyNotifySchedule()
                    notify.date = form.cleaned_data[f]
                    survey.surveynotifyschedule_set.add(notify)

            return HttpResponseRedirect('../')
    else:
        form = SurveyForm()

    return render_to_response('survey_edit.template', {'form' : form, 'title' : 'Create a New Survey' })

def survey_edit(request, id):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    index = int(id)
    survey = Survey.objects.get(id__exact = index)

    def notify_schedules(survey):
        ss = survey.surveynotifyschedule_set.all()
        result = {}
        for i in xrange(0, min(3, ss.count())):
            result['notify_%d' % (i + 1,)] = ss[i].date
        return result

    if request.method == 'POST':
        form = SurveyForm(request.POST, instance = survey)
        if form.is_valid():
            survey = form.save()
            survey.surveynotifyschedule_set.all().delete()
            for f in ['notify_1', 'notify_2', 'notify_3']:
                if form.cleaned_data[f]:
                    notify = SurveyNotifySchedule()
                    notify.date = form.cleaned_data[f]
                    survey.surveynotifyschedule_set.add(notify)
            return HttpResponseRedirect('../../')
    else:
        form = SurveyForm(instance = survey, initial = notify_schedules(survey))

    return render_to_response('survey_edit.template', {'form' : form, 'title' : 'Edit Survey: %s' % survey.title })


def survey_view(request, id):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    index = int(id)
    survey = Survey.objects.get(id__exact = index)
    form = CreateFormForSurveyTemplate(survey.template)
    return render_to_response('survey_view.template', { 'form' : form, 'survey' : survey})

def survey_interviewee(request, id):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    survey_id = int(id)
    survey = Survey.objects.get(id__exact = survey_id)

    params = dict(request.GET.items())
    order_field, order_type = extract_ordering(params)
    query = request.GET.get(SEARCH, '')

    filter_words = extract_filter(params)
    interviewees = survey.surveyinvitation_set.filter(**filter_words)

    if query:
        interviewees = apply_search(interviewees, query, ('name', 'email'))

    if order_field:
        orderby = '%s%s' % (order_type == 'desc' and '-' or '', order_field)
        interviewees = interviewees.order_by(orderby)

    p_result = {'record_in_page' : interviewees.count(), 'record_number' : interviewees.count() }

    filter_text = ''
    for f in [FilterSpec.create(SurveyInvitation._meta.get_field(f), request, params, SurveyInvitation, SurveyInvitationAdmin) for f in SurveyInvitationAdmin.list_filter]:
        filter_text += f.output(cl_for_filter_spec(params))

    headers = construct_headers(params, [SurveyInvitation._meta.get_field(f) for f in SurveyInvitationAdmin.list_display],
                                order_field, order_type)
    return render_to_response('survey_interviewee.template', {'survey' : survey,
                                                              'interviewee_list' : interviewees,
                                                              'headers' : headers,
                                                              'filter_text' : filter_text,
                                                              'paginator_result' : p_result,
                                                              'filters' : filter_words})

def survey_close(request, id):
    
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    survey_id = int(id)
    survey = Survey.objects.get(id__exact = survey_id)
    survey.closed = True
    survey.save()
    return HttpResponseRedirect('../../')

def survey_delete(request):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    if request.method != 'POST':
        return HttpResponseRedirect("../")
    selected_surveys = request.POST.getlist('_selected_survey')
    Survey.objects.filter(id__in = selected_surveys).delete()

    return HttpResponseRedirect('../')

def survey_answer(request, id):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    index = int(id)
    survey = Survey.objects.get(id__exact = index)
    if survey.closed:
        return HttpResponse('The survey you request is closed.')

    form = CreateFormForSurveyTemplate(survey.template)
    if request.method == 'GET':
        return render_to_response('survey_answer.template', {'survey' : survey, 'form' : form})
    else:
        if 'email' in request.GET:
            try:
                interviewee = SurveyInvitation.objects.get(email__exact = request.GET.get('email'))
                interviewee.answered = True
                interviewee.save()
            except SurveyInvitation.DoesNotExist:
                pass

        survey.n_answers += 1
        survey.save()
        for question in survey.template.surveyquestion_set.all():
            if str(question.index) in request.POST:
                answer = SurveyAnswer()
                answer.survey_id = survey.id
                answer.question_id = question.id
                answer.answer = request.POST.get(str(question.index))
                answer.save()

        return render_to_response('survey_thanks_answer.template', {'survey' : survey})

def survey_invite(request, id):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    index = int(id)
    survey = Survey.objects.get(id__exact = index)

    params = dict(request.GET.items())
    order_field, order_type = extract_ordering(params)
    query = request.GET.get(SEARCH, '')

    filter_words = extract_filter(params)
    students = Student.objects.filter(**filter_words)

    if query:
        students = apply_search(students, query, ('name_en', 'name_ch', 'program__name'))

    if order_field:
        orderby = '%s%s' % (order_type == 'desc' and '-' or '', order_field)
        studentss = students.order_by(orderby)

    p_result = {'record_in_page' : students.count(), 'record_number' : students.count() }

    filter_text = ''
    for f in [FilterSpec.create(Student._meta.get_field(f), request, params, Student, StudentAdmin) for f in StudentAdmin.list_filter]:
        filter_text += f.output(cl_for_filter_spec(params))

    headers = construct_headers(params, [Student._meta.get_field(f) for f in StudentAdmin.list_display],
                                order_field, order_type)

    return render_to_response('survey_invite.template', {
                'student_list' : students,
                'survey' : survey,
                'paginator_result' : p_result,
                'headers' : headers,
                'filters' : filter_words,
                'filter_text' : filter_text,
                })


"survey/invite/id/ok/"
def survey_invite_ok(request, id):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    if request.method != 'POST':
        return HttpResponseRedirect('../../../')

    index = int(id)
    survey = Survey.objects.get(id__exact = index)
    for student in Student.objects.filter(id__in = request.POST.getlist('_selected_student')):
        invitation = SurveyInvitation()
        invitation.interviewee_id = student.id
        invitation.email = student.email
        invitation.name = student.name_en
        survey.surveyinvitation_set.add(invitation)

    survey.n_invitations = survey.surveyinvitation_set.count()
    survey.save()

    return HttpResponseRedirect('../../../')


def survey_report(request, id):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    index = int(id)
    survey = Survey.objects.get(id__exact = index)

    r = []
    from crmv2.survey.report import AnswerAggragator
    for q in survey.template.surveyquestion_set.all():
        r.append('<p><strong>%d. %s</strong></p>' % (q.index, q.label))
        r.append(AnswerAggragator.create(q, q.surveyanswer_set.filter(survey__id__exact = survey.id)).output())
        r.append('<p><br /></p>')
    return render_to_response('survey_report.template', {'survey' : survey, 'report' : ''.join(r)})

def survey_template_import(request):
    try:
        request.session['id']
    except:
        return HttpResponseRedirect('/')
    
    if request.method == 'GET':
        return render_to_response('survey_template_import.template')

    else:
        try:
            file_obj = request.FILES.get('file', None)
        except:
            raise
            return render_to_response('survey_template_import.template',
                    {'message':'fileupload error!'})
        
        if file_obj:
            from xml.dom import minidom
            import StringIO
            
            try:
                buf = StringIO.StringIO(file_obj.read())
                xmldoc = minidom.parse(buf)
            except:
                return render_to_response('survey_template_import.template',
                    {'message':'You need to upload a xml file!'})
            
            try:
                template = xmldoc.getElementsByTagName('template')[0]
                name = template.getElementsByTagName('name')[0].childNodes[0].data
                description = template.getElementsByTagName('description')[0].childNodes[0].data
                
                templateObj = SurveyTemplate(
                        name = name,
                        description = description,
                        )
                templateObj.save()
                
                questionsNode = template.getElementsByTagName('questions')[0]
                questions = questionsNode.getElementsByTagName('question')
                for question in questions:
                    quesIndex = question.attributes["index"].value
                    quesType = question.attributes["type"].value
                    quesLabel = question.getElementsByTagName("label")[0].childNodes[0].data
                    questionObj = SurveyQuestion(
                            template_id = templateObj.id,
                            index = int(quesIndex),
                            type = quesType,
                            label = quesLabel
                            )
                    questionObj.save()
                    
                    if quesType in ("S","R"):
                        choice = question.getElementsByTagName("choice")[0]
                        options = choice.getElementsByTagName("option")
                        for option in options:
                            optIndex = option.getElementsByTagName("index")[0].childNodes[0].data
                            optLabel = option.getElementsByTagName("label")[0].childNodes[0].data
                            choiceObj = SurveyChoice(
                                        question_id = questionObj.id,
                                        index = int(optIndex),
                                        label = optLabel
                                        )
                            choiceObj.save()
                    
                return render_to_response('survey_template_import.template',{'message':'succeed!'})
            except:
                raise
                return render_to_response('survey_template_import.template',
                    {'message':'some node data error'})
        else:
            return render_to_response('survey_template_import.template',
                {'message':'please upload a xml file'})
    
